package lesson.base.paramresolve;



import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 请求的方法参数包装类
 */
public class ParamDefinition {

    /**
     * 参数名
     */
    private String name;
    /**
     * 参数是否来自url参数，类似 /user/1-true 这种方式的传入的参数。
     * 默认是-1，代表不是url参数，>-1时，就代表是url参数
     */
    private int urlParamIndex = -1;

    /**
     * 方法参数
     */
    private Parameter parameter;


    private ParamFeature paramFeature;



    public ParamDefinition(Parameter param) {

        this.parameter = param;

        resolveParam();
    }




    /**
     * 解析这个参数
     */
    private void resolveParam() {
        this.name = this.parameter.getName();
        UrlParamIndex index = this.parameter.getDeclaredAnnotation(UrlParamIndex.class);
        if (index != null) {
            this.urlParamIndex = index.value();
        }

        this.paramFeature = analyseParamFeatrue(this.parameter.getParameterizedType());

    }



    /**
     * 解析参数特征
     * @param type
     */
    private ParamFeature analyseParamFeatrue(Type type) {
        ParamFeature feature = new ParamFeature();

        Class clazz = null;

        if (type instanceof Class) {

            clazz = (Class) type;
            feature.setParamType(ParamType.analyseParamType(clazz));

        } else if (type instanceof ParameterizedType) {

            clazz = (Class)((ParameterizedType) type).getRawType();
            feature.setParamType(ParamType.analyseParamType(clazz));

        } else if(type instanceof GenericArrayType) {

            feature.setParamType(ParamType.ARRAY);

        } else {

            throw new RuntimeException("不支持进行参数转化的类型：" + type);

        }

        feature.setClazz(clazz);


        if (feature.getParamType() != ParamType.COMMON) {
            feature.setSubParamFeatures(analyzeSubFeatures(feature.getParamType(), type));
        }
        return feature;
    }


    /**
     * 解析子函数并生成特征
     * @param paramType
     * @param type
     * @return
     */
    private ParamFeature[] analyzeSubFeatures(ParamType paramType, Type type) {

        switch (paramType) {
            case POJO:

                return analyzePojoFeatures(type);

            case LIST:

                return analyzeListElementFeatures(type);

            case MAP:

                return analyzeMapEntryFeatures(type);

            case ARRAY:

                return analyzeArrayComponentFeatures(type);
        }
        return null;
    }

    /**
     * 解析POJO成员变量特征
     * @param pojoType
     * @return
     */
    private ParamFeature[] analyzePojoFeatures(Type pojoType) {

        if (pojoType instanceof Class == false) {
            throw new RuntimeException("无法识别解析类型：" + pojoType);
        }
        Field[] fields = ((Class)pojoType).getDeclaredFields();
        List<ParamFeature> features = new ArrayList<>();
        for (Field field : fields) {
            int modifiers = field.getModifiers();
            if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers) ) {
                continue;
            }
            field.setAccessible(true);
            ParamFeature feature = analyseParamFeatrue(field.getGenericType());
            feature.setField(field);
            features.add(feature);
        }
        return features.toArray(new ParamFeature[0]);

    }

    /**
     * 解析Map记录的特征
     * @param mapType
     * @return
     */
    private ParamFeature[] analyzeMapEntryFeatures(Type mapType) {
        if (mapType instanceof ParameterizedType == false) {
            throw new RuntimeException("必须指定具体的泛型类型才可进行参数转化：" + mapType);
        }
        Type[] types = ((ParameterizedType)mapType).getActualTypeArguments();
        return new ParamFeature[]{
                analyseParamFeatrue(types[0]),
                analyseParamFeatrue(types[1])
        };
    }

    /**
     * 解析List的元素特征
     * @param listType
     * @return
     */
    private ParamFeature[] analyzeListElementFeatures(Type listType) {
        if (listType instanceof ParameterizedType == false) {
            throw new RuntimeException("必须指定具体的泛型类型才可进行参数转化："  + listType);
        }
        return new ParamFeature[]{
                analyseParamFeatrue(((ParameterizedType)listType).getActualTypeArguments()[0])
        };
    }

    /**
     * 解析数组的子元素特征
     * @param arrayType
     * @return
     */
    private ParamFeature[] analyzeArrayComponentFeatures(Type arrayType) {
        Type componentType = null;
        if (arrayType instanceof Class) {
            componentType = ((Class)arrayType).getComponentType();
        } else if(arrayType instanceof GenericArrayType) {
            componentType = ((GenericArrayType)arrayType).getGenericComponentType();
        }
        return new ParamFeature[]{analyseParamFeatrue(componentType)};
    }

    public String getName() {
        return name;
    }

    public int getUrlParamIndex() {
        return urlParamIndex;
    }

    public Parameter getParameter() {
        return parameter;
    }

    public ParamFeature getParamFeature() {
        return this.paramFeature;
    }

    /**
     * 参数特征
     */
    public static class ParamFeature {
        private ParamType type; //类型
        private Class clazz; //参数类
        private Field field; //是哪个字段，只有当父级paramType是 POJO时，才会用到

        /**
         * 子参数类型特征，<br/>
         * 如果paramType = Array，这里存放的就是数组元素的类型特征 <br/>
         * 如果paramType = List，这里存放的就是集合元素的类型特征 <br/>
         * 如果paramType = Map, 这里存放的就是Key 和 Value的类型特征 <br/>
         * 如果paramType = POJO, 这里存放的就是成员变量的类型特征
         */
        private ParamFeature[] subParamFeatures;


        public ParamType getParamType() {
            return type;
        }

        public void setParamType(ParamType paramType) {
            this.type = paramType;
        }

        public Class getClazz() {
            return clazz;
        }

        public void setClazz(Class clazz) {
            this.clazz = clazz;
        }

        public Field getField() {
            return field;
        }

        public void setField(Field field) {
            this.field = field;
        }

        public ParamFeature[] getSubParamFeatures() {
            return subParamFeatures;
        }

        public void setSubParamFeatures(ParamFeature[] subParamFeatures) {
            this.subParamFeatures = subParamFeatures;
        }
    }

}
